home *** CD-ROM | disk | FTP | other *** search
- {
- TITLE : Interrupt handler
- PRODUCT : Turbo Pascal
- VERSION : All
- OS : Ms DOS 1.00 or greater
- DATE : 2/1/85
-
- ----------------------------------------------------------------------------
- DumbTerm is an example program written to demonstrate the use of both
- interrupt routines and com port communication. There are some inline
- instructions that are used in the interrupt routine that do not appear in
- the Turbo manual. This is because the Turbo manual contains incomplete
- inline coding. When making your own interrupt handlers be sure to save
- all registers, as has been done in this example. There is also a
- restoration of the DS register in the handler. When an interrupt occurs
- all segment registers are set to the code segment of the interrupt routine
- this disallows the use of global variables, because of a destroyed DS
- register. To counter act this affect, the DS is restored via an absolute
- variable "segment". This program was
-
- Written by,
- Jim McCarthy
- Technical Support
- Borland International
-
- and
- Andy Batony
- Teleware Incorporated
-
- -----------------------------------------------------------------------------}
-
- PROGRAM dumbterm;
-
- CONST
- hex : string[16] = '0123456789ABCDEF'; { Constant used to convert }
- { decimal to hex }
- irq4 = $30; { Interrupt vector address for }
- { COM1. }
- irq3 = $2C; { Vector for COM2. }
- eoi = $20; { }
- com1base = $03F8; { Port address of COM1. }
- com2base = $02F8; { Port address of COM2. }
- { Offset to add to com#base for}
- intenreg = 1; { Interrupt enable register }
- intidreg = 2; { Interrupt id register }
- linectrl = 3; { Line control register }
- modemctrl = 4; { Modem control register }
- linestat = 5; { Line status register }
- modemstat = 6; { Modem status register }
- buffsize = 1024; { Size of the ring buffer }
-
- TYPE { Type declarations }
- str4 = string[4];
- str80 = string[80];
- ratetype = (rate300,rate1200,rate4800,rate9600);
- comtype = (com1,com2);
- bytechar = record case boolean of
- true :(o:byte);
- false:(c:char)
- end;
-
-
-
-
-
-
-
-
-
- regrec = record
- ax,bx,cx,dx,bp,di,si,ds,es,flags : integer;
- end;
-
- VAR
- segment : integer absolute cseg:$00A0; { Address for storing DS }
- intbuffer : array [0..buffsize] of bytechar; { Ring buffer }
- oldvecseg, { Segment of DOS set }
- oldvecoff, { Offset of DOS set com int. }
- head, { Index to the head of the }
- { ring buffer. }
- tail, { Tail index of the ring buff }
- comport, { Comport address }
- i : integer; { Counter }
- comp : comtype; { Used to specify which comport}
- ch, { Temperary character buffer }
- kch : char; { Char keyed in from the key- }
- { board }
- temp : string[80]; { Temperary buffer }
- tbyte,
- lbyte : byte;
- showok : boolean;
- registers : regrec; { Registers used in DOS call }
-
- {----------------------------------------------------------------------------
- This is the interrupt handler for the COM1 or COM2 comports. Notice
- the restoration of the DS register through a move to the AX from address
- CS:00A0. The absolute variable "segment" is initialized at the begining
- of the program to contain the value of "DSEG". The inline statments should
- replace the current ones in the Turbo reference manual.
- ----------------------------------------------------------------------------}
-
- PROCEDURE IntHandler;
-
- BEGIN
- inline( $50 { push ax }
- /$53 { push bx }
- /$51 { push cx }
- /$52 { push dx } { Save all the registers }
- /$57 { push di }
- /$56 { push si }
- /$06 { push es }
- /$1E { push ds }
- /$2E { cs: }
- /$A1 /$A0 /$00 { mov ax, [00A0] } { Get the Current data }
- /$50 { push ax } { segment }
- /$1F { pop ds } ); { Restore the DS register }
- tbyte := port[ comport ]; { Get the character in the port}
- lbyte := port[ comport + linestat ]; { Get the status of the port }
- If ( head < buffsize ) then { Check bounds of the ring }
- head := head + 1 { buffer, and if smaller then }
- else { increment by one otherwise }
- head := 0; { set to the first element }
- intbuffer[ head ].o := tbyte; { Load the buffer w/ the char. }
- port[$20] := $20; { }
- inline( $1F { pop ds }
- /$07 { pop es }
- /$5E { pop si }
-
-
-
-
-
-
-
-
- /$5F { pop di }
- /$5A { pop dx }
- /$59 { pop cx } { Restore all registers }
- /$5B { pop bx }
- /$58 { pop ax }
- /$5D { pop bp } { Reset the stack to its }
- /$89 /$EC { mov sp,bp } { proper position }
- /$5D { pop bp }
- /$CF ); { iret } { Return }
- END;
-
- {-----------------------------------------------------------------------------
- The procedure AskCom gets the comport to comunicate through.
- -----------------------------------------------------------------------------}
-
- PROCEDURE AskCom( var comp : comtype );
-
- VAR
- ch : char;
-
- BEGIN
- write( 'What port is the modem in ( 1 or 2 ) : ' ); { Write prompt }
- Repeat
- read( kbd,ch ); { Get the character and }
- Until ( ch in ['1','2'] ); { check bounds }
- If ( ch = '1' ) then
- Begin
- writeln( 'COM1:' ); { Set to COM1 }
- comp := com1;
- End
- else
- Begin
- writeln( 'COM2:' );
- comp := com2; { Set to COM2 }
- End;
- END;
-
- {-----------------------------------------------------------------------------
- This procedure sets the baud rate of the comport to either 300, 1200,
- 4800, or 9600 baud. The Divisor latches are set according to the table in
- the IBM hardware technical reference manual ( p. 1-238 ).
- -----------------------------------------------------------------------------}
-
- PROCEDURE SetRate(r:ratetype);
-
- VAR
- tlcr, { Line control register }
- tdlmsb, { Divisor latch MSB }
- tdllsb : byte; { Divisor latch LSB }
-
- BEGIN
- tdlmsb:=0; { Set DL MSB to 0 for 1200, }
- { 4800 and 9600 baud }
- case r of { Use case to check baud rate }
- rate300 : begin { Check for 300 baud }
- tdlmsb:=1; { Set DL MSB to 01 }
- tdllsb:=$80; { Set DL LSB to 80 }
- end; { for a total of 0180 }
-
-
-
-
-
-
-
-
- rate1200 : tdllsb:=$60; { 1200 set LSB to 60 }
- rate4800 : tdllsb:=$18; { 4800 set LSB to 18 }
- rate9600 : tdllsb:=$0c; { 0C for 9600 baud }
- end;
- tlcr:=port[comport+linectrl]; { Get the Line control register}
- port[comport+linectrl]:=tlcr or $80; { Set Divisor Latch Access Bit }
- port[comport]:=tdllsb; { in order to access divisor }
- port[comport+1]:=tdlmsb; { latches, then store the }
- { values for the desired baud }
- { rate }
- port[comport+linectrl]:=tlcr and $7f; { then clear the DLAB in order }
- { to access to the receiver }
- { buffer }
- END;
-
- {----------------------------------------------------------------------------
- WhatRate is the input procedure that uses SetRate to set the correct
- baud rate.
- ----------------------------------------------------------------------------}
-
- PROCEDURE WhatRate;
-
- BEGIN
- writeln; { Display prompt }
- write('what baud rate ([3]00,[1]200,[4]800,[9]600) ');
- read(kbd,kch); { Read in the baud rate }
- case kch of
- '3':SetRate(rate300); { Set the corrisponding rate }
- '1':SetRate(rate1200); { . }
- '4':SetRate(rate4800); { . }
- '9':SetRate(rate9600); { . }
- end;
- writeln(kch);
- END;
-
- {-------------------------------------------------------------------------
- The procedure IntOn sets up the interrupt handler vectors, and
- communication protocal.
- -------------------------------------------------------------------------}
-
- PROCEDURE IntOn(com:comtype);
-
- CONST
- bits5=0;
- bits6=1;
- bits7=2;
- bits8=3;
- stopbit1=0; { These are constants used }
- stopbit2=4; { to define parity, stop bits, }
- noparity=0; { data bits, etc. }
- parity=8;
- evenparity=16;
- dtrtrue=1;
- rtstrue=2;
- bit3true=8;
-
- VAR
- tbyte : byte; { Temperary byte buffer }
-
-
-
-
-
-
-
-
- i : integer; { counter }
-
- BEGIN
- head:=0; { Initialize the ring buffer }
- tail:=0; { indexes }
- case com of
- com1:comport:=com1base; { Set the com port to talk to }
- com2:comport:=com2base;
- end;
- tbyte := port[ comport ]; { Read the ports to clear any }
- tbyte := port[ comport + linestat ]; { error conditions }
- WhatRate; { Get the baud rate }
- port[ comport + linectrl ] := bits7 + stopbit1 + noparity;
- { Set the protocall }
- port[ comport + modemctrl ] := dtrtrue + rtstrue + bit3true;
- port[ comport + intenreg ] := 1; { Enable com port interrupts }
- tbyte := port[$21]; { }
- with registers do
- begin
- ax:=$2500; { Load the function number for }
- { redefining an interrupt }
- ds:=cseg; { Get and set the segment and }
- dx:=ofs(IntHandler); { offset of the handler }
- end;
- case com of
- com1: begin
- oldvecoff:=memw[0000:irq4]; { Save the segment and offset }
- oldvecseg:=memw[0000:irq4+2]; { of the DOS interrupt handler }
- registers.ax:=registers.ax+$0c; { Use the COM1: interrupt }
- intr($21,registers); { Call DOS to reset INT 0C }
- port[$21]:=tbyte and $ef; { }
- end;
- com2: begin
- oldvecoff:=memw[0000:irq3]; { Same as above }
- oldvecseg:=memw[0000:irq3+2]; { Same as above }
- registers.ax:=registers.ax+$0b; { Use the COM2: interrupt }
- intr($21,registers); { Call DOS }
- port[$21]:=tbyte and $f7; { }
- end;
- end;
- inline($fb); { Enable interrupts }
- END;
-
- {-----------------------------------------------------------------------------
- This procedure restores the original system values to what they
- were before the interrupt handler was set into action.
- -----------------------------------------------------------------------------}
-
- PROCEDURE IntOff;
-
- VAR
- tbyte:byte;
-
- BEGIN
- inline($FA); { CLI } { Disable interrupts }
- tbyte:=port[$21]; { }
- port[comport+intenreg]:=0; { Disable COM interrupts }
- If comport=$3f8 then { If using COM1: then }
-
-
-
-
-
-
-
-
- begin
- port[$21]:=tbyte or $10; { }
- memw[0000:irq4]:=oldvecoff; { Restore the DOS interrupt }
- memw[0000:irq4+2]:=oldvecseg; { handler }
- end
- else
- begin
- memw[0000:irq3]:=oldvecoff; { Restore the DOS interrupt }
- memw[0000:irq3+2]:=oldvecseg; { handler }
- port[$21]:=tbyte or $08; { }
- end;
- END;
-
- {-----------------------------------------------------------------------------
- If the ring buffer indexes are not equal then ReadCom returns the
- char from either the COM1: or COM2: port. The character is read from the
- ring buffer and is stored in the FUNCTION result.
- -----------------------------------------------------------------------------}
-
- FUNCTION ReadCom : char;
-
- BEGIN
- If ( head <> tail ) then { Check for ring buffer character }
- begin
- If ( tail < buffsize ) then { Check the limits of the ring }
- tail := tail + 1 { and set tail accordingly }
- else
- tail := 0;
- ReadCom := intbuffer[tail].c; { Get the character }
- end;
- END;
-
- {----------------------------------------------------------------------------
- This procedure outputs directly to the communications port the byte
- equivilent of the character to be sent.
- ----------------------------------------------------------------------------}
-
- PROCEDURE WriteCom( ch : char );
-
- VAR
- tbyte:byte;
-
- BEGIN
- tbyte:=ord(ch); { Change to byte format }
- port[comport]:=tbyte; { Output the character }
- END;
-
- {---------------------------------------------------------------------------
- When the interrupt routine is called because of a com port interrupt
- the head index is incremented by one, but does not increment the tail
- index. This causes the two indexes to be unequal, and ModemInput to
- become true.
- ---------------------------------------------------------------------------}
-
- FUNCTION ModemInput:boolean;
-
- begin
- ModemInput:=(head<>tail);
-
-
-
-
-
-
-
-
- end;
-
- BEGIN
- segment := dseg; { segment is an absolute variable used }
- { by the interrupt routine to restore }
- { the DS register to point to the DSEG }
- AskCom( comp ); { Get the com port to use }
- IntOn( comp ); { Set up the interrupt routine }
- ch:=' '; { Initialize ch for the loop }
- Repeat
- If keypressed then { If a key is pressed on the keyboard }
- begin
- read(kbd,kch); { then the program reads it in and }
- kch := Upcase(kch);
- write( kch );
- If ( kch = chr(13)) then writeln;
- { checks if the program should be ended }
- WriteCom(kch); { Write the character to the com port }
- end;
- If ModemInput then { If something was placed in the ring }
- begin { buffer then }
- write('[');
- ch:=ReadCom; { it is read in and printed to the }
- write(ch); { screen }
- If ch=chr(13) then writeln;
- write(']');
- end;
- Until kch=chr(27); { This loops ends when <esc> is hit }
- IntOff; { Restore the enviornment }
- END. { Main program }
-
-
-